package com.aforce3.trailertracer;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;

import com.jimsproch.data.StringUtilities;

/**
 * Display is the graphics window that will render the scene generated by our RayTracer.
 * 
 * @author Jim Sproch
 */
public class Display extends JFrame
{
	private static final long serialVersionUID = -1810361358483897955L;
	final Canvas canvas = new Canvas();
	final FrameServer server;
	Renderer[] renderers;
	DisplayDaemon displayDaemon = new DisplayDaemon(this);

	public Display(final FrameServer server, Renderer... renderers)
	{
		super("Sudo-Real-Time Distributed Ray-Tracing in Java");
		requestFocusInWindow();
		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		this.server = server;

		this.renderers = renderers;

		java.awt.EventQueue.invokeLater(new Runnable()
		{
			public void run()
			{
				getContentPane().setPreferredSize(new Dimension(server.getFrame(0).getWidth(null), server.getFrame(0).getHeight(null)));
				pack();
				setVisible(true);
				
				getContentPane().setBackground(Color.black);
				getContentPane().add(canvas);

				setVisible(true);

				// Set the BufferStrategy for DoubleBuffering
				try
				{
					canvas.createBufferStrategy(2);
					displayDaemon.start();
				}
				catch(IllegalStateException e)
				{
					e.printStackTrace();
					throw new RuntimeException(e);
				}
				catch(Exception e)
				{
					e.printStackTrace();
					throw new RuntimeException(e);
				}
			}
		});
	}

	public void terminate()
	{
		setVisible(false);
		for(Renderer r : renderers)
		{
			r.terminate();
			while(r.isAlive())
				try
				{
					Thread.sleep(1);
				}
				catch(Exception e)
				{
				}
		}
		displayDaemon.terminate();
		while(displayDaemon.isAlive())
			try
			{
				Thread.sleep(1);
			}
			catch(Exception e)
			{
			}
		dispose();
	}

	class DisplayDaemon extends Thread implements WindowListener
	{
		Display display;
		volatile boolean terminate = false;

		public DisplayDaemon(Display display)
		{
			this.display = display;
			display.addWindowListener(this);

			// We want the frames swapping to look smooth
			this.setPriority(MAX_PRIORITY);
		}

		public void run()
		{
			int currentFrame = 9;
			long lastFrameTime = System.currentTimeMillis();
			BufferStrategy myStrategy = canvas.getBufferStrategy();
			while(!terminate)
			{
				if(currentFrame >= server.getFrameCount()) currentFrame = 0;

				while(lastFrameTime + 25 > System.currentTimeMillis())
					try
					{
						Thread.sleep(1);
					}
					catch(Exception e)
					{
					}
				;
				lastFrameTime = System.currentTimeMillis();

				// Yep, we do need a few things before we get started
				Graphics graphics = myStrategy.getDrawGraphics();

				// Draw the next frame to the back buffer
				graphics.drawImage(server.getFrame(currentFrame), 0, 0, (int) display.getContentPane().getSize().getWidth(), (int) display.getContentPane().getSize().getHeight(), null);

				// Cleanup the drawing tool, we're done with it
				graphics.dispose();

				// Flush the buffer to the screen
				if(!myStrategy.contentsLost()) myStrategy.show();

				Toolkit.getDefaultToolkit().sync();

				currentFrame++;
			}

			File dir = new File(System.getProperty("user.home")+File.separator+"TrailerTracer");
			dir.mkdirs();
			
			long time = System.currentTimeMillis();

			for(int i = 0; i < server.getFrameCount(); i++)
			{
				try
				{
					BufferedImage bi = (BufferedImage) server.getFrame(i);
					File outputfile = new File(dir.getAbsolutePath()+File.separator+time+"-"+StringUtilities.addLeadingZeros(i, 5)+".png");
					ImageIO.write(bi, "png", outputfile);
				}
				catch(IOException e)
				{
					throw new RuntimeException(e);
				}
			}
		}

		public void terminate()
		{
			terminate = true;
		}

		public void windowActivated(WindowEvent arg0)
		{
		}

		public void windowClosed(WindowEvent arg0)
		{
		}

		public void windowClosing(WindowEvent arg0)
		{
			display.terminate();
		}

		public void windowDeactivated(WindowEvent arg0)
		{
		}

		public void windowDeiconified(WindowEvent arg0)
		{
		}

		public void windowIconified(WindowEvent arg0)
		{
		}

		public void windowOpened(WindowEvent arg0)
		{
		}
	}
}
